iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0

Table of Contents

  • 事件是什麽?
  • 事件冒泡與捕獲
  • 事件委派
  • References

前面講了很多如何控制元素的方法,今天要來講如何用事件讓使用者跟網頁互動。

事件是什麽?

DOM元素設定為監聽/接受事件,通常滿足某個條件之後會觸發事件,再執行程式碼回應事件,MDN上就有提供一個事件清單可以參考。
觸發事件大概有下面的方式:

  1. 事件由使用者動作並觸發
  2. 從API產生非同步事件
  3. 透過程式觸發,比如呼叫元素、或定義事件,再傳到指定目標

事件傳遞:冒泡、目標與捕獲

事件物件先會被分配到事件目標,在分配之前會確定傳播路徑。這個傳播路徑決定事件如何被傳遞,等同攸關事件被觸發的順序,傳遞分成三個階段:捕獲、目標、冒泡,幾乎所有的事件都會冒泡。

引用W3C的圖片:

source:UI Events

捕獲(Capture Phase):事件的傳遞會從目標的祖父開始,去尋找綁定捕獲事件的元素,會從window傳到目標的父層。
目標(Target Phase):事件物件到達該事件的目標,但如果事件是不會冒泡的類型,就會在這個階段停止傳遞,比如focus
冒泡(Bubbling Phase):會以相反的順序傳播回去,尋找是否綁定冒泡事件的元素,綁定的事件會依照這個方向一一觸發,就像從水底冒泡泡到最上層,事件傳遞最終會到window結束。

但不一定每個事件都有冒泡,那我們要怎麽知道我們想要使用的事件有沒有冒泡呢?可以看看規範,會告訴你每個事件的性質是什麽,也會告訴你有沒有冒泡。

關於事件的發生順序,我會借用javascript.info的程式碼,對畫面中的任一區塊的元素都新增一個事件,並且觀察事件發生的順序:

for(let element of document.querySelectorAll('*')) {
  element.addEventListener("click", event => console.log(`Capturing: ${element.tagName}`), true);
  element.addEventListener("click", event => console.log(`Bubbling: ${element.tagName}`));
}

下面是示範todolist的按鈕:

<body>
  <main>
    <section class="panel">
      <div class="add_task">
        <button class="add_task_button" type="button">add task</button>
      </div>
     </section>
  </main>  
</body>

畫面呈現:

點擊按鈕,產生一段捕獲及冒泡順序:

可以看到他從HTML開始捕獲,但最後結束的也是HTML。可能會想問那documentwindow呢?這兩者不是元素,沒有出現在這上面,但實際上就是照前面提到的傳遞順序。

事件委派

觸發事件的元素跟目標元素不一定一樣,例如在最外的容器設置事件,但是被點擊到的元素是event.target(事件目標),每次被點擊的不一定一樣。

假設我們現在有這組範例:

<div class="task_information"> 
    <div class="task_event">
        <input class="finish_task" type="checkbox">
        <input class="task_title" type="text" size="28" placeholder="type something here...">
    </div>
    <div class="task_mark">
        <button class="task_important"><i class="fal fa-star"></i></button>
        <button class="task_edit_button fa-solid fa-pen"></button>
    </div>
</div>

畫面呈現是:

假如我們把事件綁在task_information,那麽事件中的this(=event.currentTarget)的元素就是task_information。讓我們從下面的程式碼中示範:

taskItemInformation.addEventListener('click',handler)
function handler(event){
  console.log(event.currentTarget,this)//<div>,<div>
  console.log(event.target)//<input>
}

那事件委派的好處,就是不用一一存取綁定元素,再一個個綁定事件,只要在共用的祖父元素加上事件,在處理事件中的程式碼設置觸發條件,就可以一併處理。

另外,在Day16的文章中曾經提到querySelectorAll()因為是靜態的Nodelist,很難調整Javascript中後來新增的元素,這時也可以利用委派來解決。

回顧當時的示範程式碼,首先HTML的結構中有三個<li>,當我新增一個<li>並放進結構中,nodelist維持一樣。

<h1>contestantNumber: 1</h1>
<ul>
    <li>hotpotFlavor: Spicy Sichuan</li>
    <li>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
    <li>beveragePairing: Jasmine tea
    </li>
</ul>

這時我讓<ul>綁事件,只要在<ul>的範圍內點擊的文字就會變色。

//在最一開始取得li的list
const li = document.querySelectorAll('li');
//取外層容器作為等等新增元素的地方
const ul = document.querySelector('ul')
//新增第四個li
const newLi = document.createElement("li");
//對新的li新增文字
newLi.innerText = `extraDish: Crispy fried wontons`;
//在ul裡面新增剛剛的li
ul.appendChild(newLi);
console.log(li)//nodelist(3)

ul.addEventListener('click',(event)=>{
    //點擊後,事件目標的顏色會變成藍色
  event.target.style.color="blue";
})

點擊後的結果:

References

本文程式碼的UI參考:六角學院的todolist

DOM 的事件傳遞機制:捕獲與冒泡
What is event bubbling and capturing?
事件處理
UI Events

  • MDN
  1. Introduction to events
  2. EventTarget
  3. Event
  • JavaScript Info

Bubbling and capturing
Event delegation


上一篇
〈Day21〉元素的尺寸和位置
下一篇
〈Day23〉事件監聽
系列文
廚藝不精也可以,給自己做一份Javascript小火鍋30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言